/*
   Copyright (c) 2020, zoomdy
   MicroAmpereII is licensed under Mulan PSL v2.
   You can use this software according to the terms and conditions of the Mulan PSL v2.
   You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2
   THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
   EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
   MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
   See the Mulan PSL v2 for more details.
 */

#include "gd32vf103.h"
#include "hal_senser.h"
#include "hal_tick.h"

#define SENSER_0R1_ON_PORT              GPIOB
#define SENSER_0R1_ON_PIN               GPIO_PIN_9

#define SENSER_1R0_ON_PORT              GPIOB
#define SENSER_1R0_ON_PIN               GPIO_PIN_8

#define SENSER_10R_ON_PORT              GPIOB
#define SENSER_10R_ON_PIN               GPIO_PIN_5

#define SENSER_100R_ON_PORT             GPIOB
#define SENSER_100R_ON_PIN              GPIO_PIN_4


#define SENSER_0R1_AIN_PORT             GPIOA
#define SENSER_0R1_AIN_PIN              GPIO_PIN_0

#define SENSER_1R0_AIN_PORT             GPIOA
#define SENSER_1R0_AIN_PIN              GPIO_PIN_3

#define SENSER_10R_AIN_PORT             GPIOA
#define SENSER_10R_AIN_PIN              GPIO_PIN_4

#define SENSER_100R_AIN_PORT            GPIOA
#define SENSER_100R_AIN_PIN             GPIO_PIN_6

typedef struct
{
    uint32_t on_port;
    uint32_t on_pin;
    uint32_t ain_port;
    uint32_t ain_pin;
}hal_senser_on_gpio_t;

static const hal_senser_on_gpio_t hal_senser_on_gpio[4] = {
        {SENSER_0R1_ON_PORT, SENSER_0R1_ON_PIN, SENSER_0R1_AIN_PORT, SENSER_0R1_AIN_PIN},
        {SENSER_1R0_ON_PORT, SENSER_1R0_ON_PIN, SENSER_1R0_AIN_PORT, SENSER_1R0_AIN_PIN},
        {SENSER_10R_ON_PORT, SENSER_10R_ON_PIN, SENSER_10R_AIN_PORT, SENSER_10R_AIN_PIN},
        {SENSER_100R_ON_PORT, SENSER_100R_ON_PIN, SENSER_100R_AIN_PORT, SENSER_100R_AIN_PIN},
};

static hal_senser_chnl_t hal_senser_chnl_current;

#define HAL_SENSER_VALUE_NUM            64
static uint16_t hal_senser_values[HAL_SENSER_VALUE_NUM] __attribute__((aligned(8)));

static void hal_senser_gpio_init(void);
static void hal_senser_timer_init(void);
static void hal_senser_dma_init(void);
static void hal_senser_adc_init(void);
void hal_senser_start(void);
static void hal_senser_fifo_init(void);


void hal_senser_init_(void)
{
    hal_senser_gpio_init();
    hal_senser_timer_init();
    hal_senser_dma_init();
    hal_senser_adc_init();

    hal_senser_start();

    hal_senser_fifo_init();
    hal_senser_chnl_current = HAL_SENSER_CHNL_NULL;
}


void hal_senser_set_chnl(hal_senser_chnl_t chnl)
{
    if(chnl == hal_senser_chnl_current) {
        return;
    }

    if(chnl != HAL_SENSER_CHNL_NULL) {
        gpio_bit_reset(hal_senser_on_gpio[chnl].on_port, hal_senser_on_gpio[chnl].on_pin);
    }

    if(hal_senser_chnl_current != HAL_SENSER_CHNL_NULL) {
        gpio_bit_set(hal_senser_on_gpio[hal_senser_chnl_current].on_port,
                hal_senser_on_gpio[hal_senser_chnl_current].on_pin);
    }

    hal_senser_chnl_current = chnl;
}


hal_senser_chnl_t hal_senser_get_chnl(void)
{
    return hal_senser_chnl_current;
}


uint16_t hal_senser_get(hal_senser_chnl_t chnl)
{
    return 0U;
}


void hal_senser_start(void)
{
    /* TIMER1 counter enable */
    timer_enable(TIMER1);
}


void hal_senser_stop(void)
{
    /* TIMER1 counter disable */
    timer_disable(TIMER1);
}


bool hal_senser_half_complete(void)
{
    bool complete = false;

    if(dma_flag_get(DMA0,DMA_CH0, DMA_FLAG_HTF)) {
        dma_flag_clear(DMA0,DMA_CH0, DMA_FLAG_HTF);
        complete = true;
    }

    return complete;
}


bool hal_senser_full_complete(void)
{
    bool complete = false;

    if(dma_flag_get(DMA0,DMA_CH0, DMA_FLAG_FTF)) {
        dma_flag_clear(DMA0,DMA_CH0, DMA_FLAG_FTF);
        complete = true;
    }

    return complete;
}


static void hal_senser_gpio_init(void)
{
    int i;

    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_GPIOB);

    for(i = 0; i < 4; i++) {
        gpio_bit_set(hal_senser_on_gpio[i].on_port, hal_senser_on_gpio[i].on_pin);
        gpio_init(hal_senser_on_gpio[i].on_port, GPIO_MODE_OUT_OD, GPIO_OSPEED_2MHZ, hal_senser_on_gpio[i].on_pin);
        gpio_bit_set(hal_senser_on_gpio[i].on_port, hal_senser_on_gpio[i].on_pin);

        gpio_init(hal_senser_on_gpio[i].ain_port, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, hal_senser_on_gpio[i].ain_pin);
    }
}


static void hal_senser_timer_init(void)
{
    timer_oc_parameter_struct timer_ocintpara;
    timer_parameter_struct timer_initpara;

    rcu_periph_clock_enable(RCU_TIMER1);

    timer_deinit(TIMER1);

    /* TIMER1 configuration */
    timer_initpara.prescaler         = 96 - 1;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 1000 - 1;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER1,&timer_initpara);

    /* CH1 configuration in PWM mode1 */
    timer_channel_output_struct_para_init(&timer_ocintpara);
    timer_ocintpara.ocpolarity  = TIMER_OC_POLARITY_LOW;
    timer_ocintpara.outputstate = TIMER_CCX_ENABLE;
    timer_channel_output_config(TIMER1, TIMER_CH_1, &timer_ocintpara);

    timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_1, 500);
    timer_channel_output_mode_config(TIMER1, TIMER_CH_1, TIMER_OC_MODE_PWM1);
    timer_channel_output_shadow_config(TIMER1, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE);

    /* auto-reload preload enable */
    timer_auto_reload_shadow_enable(TIMER1);
}


static void hal_senser_dma_init(void)
{
    /* ADC_DMA_channel configuration */
    dma_parameter_struct dma_data_parameter;

    rcu_periph_clock_enable(RCU_DMA0);

    /* ADC DMA_channel configuration */
    dma_deinit(DMA0, DMA_CH0);

    /* initialize DMA single data mode */
    dma_data_parameter.periph_addr  = (uint32_t)(&ADC_RDATA(ADC0));
    dma_data_parameter.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_data_parameter.memory_addr  = (uint32_t)(&hal_senser_values[0]);
    dma_data_parameter.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
    dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;
    dma_data_parameter.direction    = DMA_PERIPHERAL_TO_MEMORY;
    dma_data_parameter.number       = HAL_SENSER_VALUE_NUM;
    dma_data_parameter.priority     = DMA_PRIORITY_HIGH;
    dma_init(DMA0, DMA_CH0, &dma_data_parameter);
    dma_circulation_enable(DMA0, DMA_CH0);

    /* enable DMA channel */
    dma_channel_enable(DMA0, DMA_CH0);
}


static void hal_senser_adc_init(void)
{
    /* ADC clk 12Mhz */
    rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);

    rcu_periph_clock_enable(RCU_ADC0);

    /* reset ADC */
    adc_deinit(ADC0);
    /* ADC mode config */
    adc_mode_config(ADC_MODE_FREE);
    /* ADC contineous function enable */
    adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, DISABLE);
    /* ADC scan mode disable */
    adc_special_function_config(ADC0, ADC_SCAN_MODE, DISABLE);
    /* ADC data alignment config */
    adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
    adc_oversample_mode_config(ADC0, ADC_OVERSAMPLING_ALL_CONVERT,
            ADC_OVERSAMPLING_SHIFT_8B, ADC_OVERSAMPLING_RATIO_MUL256);
    adc_oversample_mode_enable(ADC0);

    /* ADC channel length config */
    adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1);

    /* ADC regular channel config */
    adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_0/*FIXME*/, ADC_SAMPLETIME_13POINT5);
    /* ADC trigger config */
    adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_EXTTRIG_REGULAR_T1_CH1);
    adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);

    /* enable ADC interface */
    adc_enable(ADC0);
    hal_tick_delay(2);
    /* ADC calibration and reset calibration */
    adc_calibration_enable(ADC0);

    /* ADC DMA function enable */
    adc_dma_mode_enable(ADC0);
}


#define HAL_SENSER_FIFO_SIZE        256
typedef struct
{
    uint32_t head;
    uint32_t tail;
    uint16_t buffer[HAL_SENSER_FIFO_SIZE];

}hal_senser_fifo_t;

static hal_senser_fifo_t hal_senser_fifo;


static void hal_senser_fifo_init(void)
{
    hal_senser_fifo_t* fifo = &hal_senser_fifo;

    fifo->head = 0;
    fifo->tail = 0;
}


static bool hal_senser_fifo_put(uint16_t in_data)
{
    hal_senser_fifo_t* fifo = &hal_senser_fifo;
    uint32_t head = fifo->head + 1;
    head = head >= HAL_SENSER_FIFO_SIZE ? 0 : head;

    if (fifo->tail == head) {
        return false;
    } else {
        fifo->buffer[fifo->head] = in_data;
        fifo->head = head;
        return true;
    }
}


bool hal_senser_fifo_get(uint16_t* out_data)
{
    hal_senser_fifo_t *fifo = &hal_senser_fifo;
    uint32_t tail;
    if (fifo->tail == fifo->head) {
        return false;
    } else {
        tail = fifo->tail + 1;
        tail = tail >= HAL_SENSER_FIFO_SIZE ? 0 : tail;
        *out_data = fifo->buffer[fifo->tail];
        fifo->tail = tail;
        return true;
    }
}


void hal_senser_poll_(void)
{
    int i;
    uint16_t ain;

    if(hal_senser_half_complete()) {
        for(i = 0; i < HAL_SENSER_VALUE_NUM / 2; i++) {
            ain = hal_senser_values[i];
            hal_senser_fifo_put(ain);
        }
    }

    if(hal_senser_full_complete()) {
        for(i = HAL_SENSER_VALUE_NUM / 2; i < HAL_SENSER_VALUE_NUM; i++) {
            ain = hal_senser_values[i];
            hal_senser_fifo_put(ain);
        }
    }
}
